#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"

#ifndef MAX_PLAYER
#define MAX_PLAYER 4
#endif

int checkMine(struct gameState *post, int choice1, int choice2, int handPos)
{
	struct gameState pre;
	int i, result;
	int expected = 0;

	// Save fullDeckCount
	int preHandCount = post->handCount[post->whoseTurn];
	int preDeckCount = post->deckCount[post->whoseTurn];
	int preDiscardCount = post->discardCount[post->whoseTurn];
	int prePlayedCardCount = post->playedCardCount;
	int preTreasureCount = post->supplyCount[choice1];
	int cardToTrash = post->hand[post->whoseTurn][choice1];

	/* Copy memory located at post to memory located at pre */
	memcpy(&pre, post, sizeof(struct gameState));

	if (post->hand[post->whoseTurn][choice1] < copper || post->hand[post->whoseTurn][choice1] > gold ||
		choice2 > treasure_map || choice2 < curse || (getCost(post->hand[post->whoseTurn][choice1]) + 3) > getCost(choice2)
		|| post->supplyCount[choice2] < 1 || choice2 < copper || choice2 > gold) {
		expected = -1;
	}

	result = cardEffect(mine, choice1, choice2, 0, post, handPos, NULL);
	assert(result == expected);

	if (result == 0) {
		assert(post->handCount[post->whoseTurn] == preHandCount - 1);
		assert(post->deckCount[post->whoseTurn] == preDeckCount);
		assert(post->discardCount[post->whoseTurn] == preDiscardCount);
		assert(post->hand[post->whoseTurn][handPos] == choice2);
		assert(post->supplyCount[choice2] == preTreasureCount - 1);
		assert(post->playedCardCount = prePlayedCardCount + 1);
	
		/* Set Mine pre case */
		pre.hand[pre.whoseTurn][pre.handCount[pre.whoseTurn]] = choice2;
		pre.supplyCount[choice2]--;
		discardCard(handPos, pre.whoseTurn, &pre, 0);

		for (i = 0; i < pre.handCount[pre.whoseTurn]; i++) {
			if (pre.hand[pre.whoseTurn][i] == cardToTrash) {
				discardCard(i, pre.whoseTurn, &pre, 1);
				break;
			}
		}
	}
	assert(memcmp(&pre, post, sizeof(struct gameState)) == 0);
	
	return 0;
}

void sigabrt_catch(int sig)
{
	printf("SIGABRT has been caught\n");
	exit(0);
}

int main(int argc, char **argv)
{
	int i, j, n, choice1, choice2, handPos;
	struct gameState G;

	signal(SIGABRT, sigabrt_catch);

	printf("Testing cardEffect mine.\n");

	printf("RANDOM TESTS.\n");

	/* Set Random Number Generator */
	SelectStream(2);
	PutSeed(3);

	for (n = 1; n <= 20000; n++) {
		for (i = 0; i < sizeof(struct gameState); i++) {
			((char *) &G)[i] = floor(Random() * 256);
		}

		/* Randomly Set Game Numbers */
		G.numPlayers = floor(Random() * (MAX_PLAYER - 1)) + 2;
		G.whoseTurn = floor(Random() * G.numPlayers);
		G.deckCount[G.whoseTurn] = floor(Random() * MAX_DECK);
		G.discardCount[G.whoseTurn] = floor(Random() * MAX_DECK);
		G.handCount[G.whoseTurn] = floor(Random() * MAX_HAND) + 1;
		G.playedCardCount = floor(Random() * MAX_DECK);

		/* Fill deck with cards */
		for (i = 0; i < G.deckCount[G.whoseTurn]; i++) {
			G.deck[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Fill discard with cards */
		for (i = 0; i < G.discardCount[G.whoseTurn]; i++) {
			G.discard[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Fill hand with cards */
		for (i = 0; i < G.handCount[G.whoseTurn]; i++) {
			G.hand[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Set 1 random card in hand as mine */
		handPos = floor(Random() * G.handCount[G.whoseTurn]);
		G.hand[G.whoseTurn][handPos] = mine;

		/* Set random choices */
		choice1 = floor(Random() * G.handCount[G.whoseTurn]);
		while (choice1 == handPos) {
			choice1 = floor(Random() * G.handCount[G.whoseTurn]);
		}

		j = floor(Random() * 10);
		if (j <= 1) {
			choice2 = curse - 1;
		} else if (j >= 8) {
			choice2 = treasure_map + 1;
		} else if (j % 2 == 0) {
			choice2 = floor(Random() * adventurer);
		} else {
			choice2 = floor(Random() * (treasure_map - adventurer)) + adventurer;
		}

		if (argc > 1 && strcmp(argv[1], "-v") == 0) {
			printf("Test #%5d: plyr = %d dckCnt = %3d hndCnt = %3d disCnt = %3d plyCnt = %3d\n", 
					n, G.whoseTurn, G.deckCount[G.whoseTurn], G.handCount[G.whoseTurn], G.discardCount[G.whoseTurn], G.playedCardCount);
		}
		
		checkMine(&G, choice1, choice2, handPos);
	}

	printf ("ALL TESTS OK\n");

	return 0;
}